/**
 * The contents of this file are subject to the OpenMRS Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://license.openmrs.org
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * Copyright (C) OpenMRS, LLC.  All Rights Reserved.
 */

package org.openmrs.module.clinicalsummary.evaluator.velocity;

import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.ClassUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.Concept;
import org.openmrs.ConceptDatatype;
import org.openmrs.ConceptName;
import org.openmrs.ConceptNumeric;
import org.openmrs.Obs;
import org.openmrs.api.context.Context;
import org.openmrs.module.Module;
import org.openmrs.module.ModuleFactory;

public class VelocityUtils {

	private static final Log log = LogFactory.getLog(VelocityUtils.class);

	private static final String SIMPLE_DATETIME_FORMAT = "dd-MMM-yyyy hh:mm:ss.SSSS";

	private static final String SIMPLE_DATE_FORMAT = "dd-MMM-yyyy";

	/**
	 * Format obs object and return string representation of the obs.
	 *
	 * @param obs the obs to be formatted
	 * @return the string representation of the obs
	 */
	public String format(Obs obs) {

		Concept concept = obs.getConcept();

		ConceptDatatype type = null;
		if (concept != null) {
			type = concept.getDatatype();

			if (type.isCoded())
				return format(obs.getValueCoded());
			else if (type.isNumeric()) {
				ConceptNumeric conceptNumeric = Context.getConceptService().getConceptNumeric(concept.getConceptId());
				if (!conceptNumeric.isPrecise())
					return format(obs.getValueNumeric());
				else {
					DecimalFormat decimalFormat = new DecimalFormat("#.00");
					return decimalFormat.format(obs.getValueNumeric());
				}
			} else if (type.isDate() || type.isDateTime())
				return format(obs.getValueDatetime());
		}

		return StringUtils.EMPTY;
	}

	/**
	 * Format double object and remove the decimal section of the value
	 *
	 * @param value the decimal to be formatted
	 * @return String representation of the decimal values
	 */
	public String format(final Double value) {
		if (value == null)
			return StringUtils.EMPTY;
		DecimalFormat decimalFormat = new DecimalFormat("#");
		return decimalFormat.format(value);
	}

	/**
	 * Format date to a string according to the date format
	 *
	 * @param date the date
	 * @return string representation of the date
	 */
	public String format(final Date date) {
		if (date == null)
			return StringUtils.EMPTY;
		String format = SIMPLE_DATE_FORMAT;
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
		return simpleDateFormat.format(date);
	}

	/**
	 * Format the name of a concept with the best name for the locale or a shorter name for the concept
	 *
	 * @param concept the concept
	 * @return concept name with a relatively shorter name
	 */
	public String format(final Concept concept) {
		if (concept == null)
			return StringUtils.EMPTY;
		// use the best name as the default name
		String name = concept.getName(Context.getLocale()).getName();
		// when the name is too long, then use the concept's short name for display
		if (StringUtils.length(name) > 10) {
			ConceptName conceptName = concept.getShortNameInLocale(Context.getLocale());
			if (conceptName != null)
				name = conceptName.getName();
		}
		return StringEscapeUtils.escapeXml(name);
	}

	/**
	 * Extract a property of an object and return the String representation of the property value
	 *
	 * @param object   the object
	 * @param property the property name
	 * @return the string representation of the property value
	 */
	public String extractProperty(final Object object, final String property) {
		try {
			Object propertyValue = PropertyUtils.getProperty(object, property);
			if (propertyValue != null) {
				if (ClassUtils.isAssignable(propertyValue.getClass(), Date.class))
					return format((Date) propertyValue);
				else if (ClassUtils.isAssignable(propertyValue.getClass(), Concept.class))
					return format((Concept) propertyValue);
			}
			return StringEscapeUtils.escapeXml(BeanUtils.getProperty(object, property));
		} catch (Exception e) {
			return StringUtils.EMPTY;
		}
	}

	/**
	 * Get the current version of a module based on the module id
	 *
	 * @param moduleId the module id
	 * @return the module version number
	 */
	public String getModuleVersion(final String moduleId) {
		Module module = ModuleFactory.getModuleById(moduleId);
		if (module != null)
			return module.getVersion();
		return StringUtils.EMPTY;
	}

	/**
	 * Get the current date and time based on the server clock
	 *
	 * @return the current date and time
	 */
	public String getCurrentDatetime() {
		String format = SIMPLE_DATETIME_FORMAT;
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format);
		return simpleDateFormat.format(new Date());
	}

}
